package com.aldobucchi.graph.query
{


	import com.aldobucchi.graph.filters.FilterEQ;
	import com.aldobucchi.graph.restrictionTree.RTArcNode;
	import com.aldobucchi.graph.restrictionTree.RTFilterNode;
	import com.aldobucchi.graph.restrictionTree.RTVarNode;
	import com.aldobucchi.utils.Arc;
	import com.aldobucchi.utils.priority.AbstractPrioritizable;
	
	

/**
 * This is a temporary event dispatched when a result is obtained after evaluating a Query object.
 * If the query contains an aggregate function, then the result is a Number
 * If the query returns a nodeset, then the result is an array of nodes
 * If the query returned NULL ( any error for now ) then the result is NULL
 * 
 * 
 *  @eventType com.aldobucchi.graph.query.QueryEvent,RESULT
 */
[Event(name="result", type="com.aldobucchi.graph.query.QueryEvent")]	
	
	
	

/**
 *  @eventType com.aldobucchi.graph.query.QueryEvent,CANCEL
 */
[Event(name="cancel", type="com.aldobucchi.graph.query.QueryEvent")]	
	
	
/**
 *  @eventType com.aldobucchi.graph.query.QueryEvent,ERROR
 */
[Event(name="error", type="com.aldobucchi.graph.query.QueryEvent")]	







	

	/**
	 * 
	 * For now we are stuffing everything into one query object: model, status, result, etc
	 * 
	 * 
	 * @author aldo.bucchi@gmail.com
	 * 
	 */
	public class Query extends AbstractPrioritizable
	{


		
		public static const STATUS_NEW		:String = "new";

		public static const STATUS_EXECUTING	:String = "executing";
		
		public static const STATUS_CANCELED	:String = "canceled";
		
		public static const STATUS_ANSWERED	:String = "answered";

		public static const STATUS_ERROR		:String = "error";
		
		
		
		
		



		private var _target:IQueryTarget;
		
		public function get target( ):IQueryTarget
		{
			return _target;
		}
		
		
		// this is public, we should seal it somehow
		private var _result:*;
		[Bindable("result")]
		public function get result( ):*
		{
			return _result;
		}
		
		
		
		
		private var _status:String = STATUS_NEW;
		[Bindable("statusChanged")]
		public function get status( ):String
		{
			return _status;
		}
		
		
		
		
		
		
		
		
		
		
		


		public function Query( target:IQueryTarget = null, priority:uint = 5 )
		{
			_target = target == null ? defaultTarget : target;
			super( priority );
		}



		private function setStatus( newStatus:String ):Boolean
		{
			if ( _status == newStatus )
				return false;
				
			_status = newStatus;
			
			dispatchEvent( new QueryEvent( QueryEvent.STATUS_CHANGED ) );		
			
			return true;
		}



		public function cancel( ):void
		{
			if ( status == STATUS_EXECUTING )
			{
				setStatus( STATUS_CANCELED );
				dispatchEvent( new QueryEvent( QueryEvent.CANCEL ) );		
			}
		}


		public function answer( result:* ):void
		{
			if ( status == STATUS_EXECUTING )
			{
				_result = result;
				setStatus( STATUS_ANSWERED );
				dispatchEvent( new QueryEvent( QueryEvent.RESULT ) );
			}
		}

		public function execute( ):void
		{
			if ( status == STATUS_NEW )
			{
				setStatus( Query.STATUS_EXECUTING );
				target.query( this );
			}
		}



		public var aggregate				:String;
		
		public var selectVar				:RTVarNode;
		
		public var distinct				:Boolean = false;

		public var restrictionTreeRoot		:RTVarNode;		
		
		public var limit					:int = -1;
		
		public var offset					:int = -1;

		public var orderBy					:RTVarNode;




		public var selectPredicate			:Boolean = false;




		/**
		 * 
		 * Dev time.
		 * Until the rt2airdfsql parser supports inverted arcs, this is used
		 * for somple triple mask queries ( s,p,* ) or ( *,p,o )
		 * 
		 */
		public var simpleQueryNode		:* = undefined;
		public var simpleQueryArc		:Arc;





		/**
		 * This should be reviewed
		 */
		public static var defaultTarget:IQueryTarget;








		/**
		 * 
		 * Returns a query configured so that the selectVar is the one located after taking the arcs
		 * 
		 * @param node
		 * @param arcs
		 * @return 
		 * 
		 */
		public static function forPath( node:*, arcs:Array, target:IQueryTarget=null, priority:uint=5 ):Query
		{
			
			var q:Query = new Query( target, priority );
			// first restriction is on the node
			if ( arcs.length == 1 ) // use the "simple..." stuff
			{
				q.simpleQueryNode = node;
				q.simpleQueryArc = arcs[0] as Arc;
				return q;
			}
			
			
			var rootVarNode	:RTVarNode = new RTVarNode( );
			var filterNode	:RTFilterNode = new RTFilterNode( new FilterEQ( node ) );
			rootVarNode.children.addItem( filterNode );			
								
			
			var currentVarNode:RTVarNode = rootVarNode;
			
			for each ( var arc:Arc in arcs )
			{
				var arcNode		:RTArcNode = new RTArcNode( arc );
				var linkVarNode	:RTVarNode = new RTVarNode( );

				currentVarNode.children.addItem( arcNode );
												arcNode.children.addItem( linkVarNode );
				
				currentVarNode = linkVarNode;
			}
			
			q.restrictionTreeRoot 	= rootVarNode;
			q.selectVar 			= currentVarNode;
			

			return q;
		
		}









	}

}